/**
 * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
 * No deletion without permission, or be held responsible to law.
 */
package com.jeesite.common.shiro.realm;

import com.jeesite.common.cache.CacheUtils;
import com.jeesite.common.codec.EncodeUtils;
import com.jeesite.common.codec.Sha1Utils;
import com.jeesite.common.shiro.authc.FormToken;
import com.jeesite.common.utils.SpringUtils;
import com.jeesite.modules.sys.dao.MenuDao;
import com.jeesite.modules.sys.entity.*;
import com.jeesite.modules.sys.page.BuyerUser;
import com.jeesite.modules.sys.service.CereBuyerUserService;
import com.jeesite.modules.sys.service.UserService;
import com.jeesite.modules.sys.utils.LogUtils;
import com.jeesite.modules.sys.utils.UserUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 系统认证授权实现类
 *
 * @author ThinkGem
 * @version 2018-7-11
 */
public class AuthorizingRealm extends BaseAuthorizingRealm {

    public static final String HASH_ALGORITHM = "SHA-1";
    public static final int HASH_INTERATIONS = 1024;
    public static final int SALT_SIZE = 8;

    private UserService userService;

    private CereBuyerUserService buyerUserService;

    private MenuDao menuDao;

    public AuthorizingRealm() {
        super();
//		// 设定密码校验的Hash算法与迭代次数（V4.1.4及以上版本不需要了，统一使用validatePassword验证密码）
//		HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(HASH_ALGORITHM);
//		matcher.setHashIterations(HASH_INTERATIONS);
//		this.setCredentialsMatcher(matcher);
    }

    /**
     * 获取登录凭证，将 authcToken 转换为 FormToken，参考 CAS 实现
     */
    @Override
    protected FormToken getFormToken(AuthenticationToken authcToken) {
        return super.getFormToken(authcToken);
    }

    /**
     * 用于用户根据登录信息获取用户信息<br>
     * 1、默认根据登录账号登录信息，如：UserUtils.getByLoginCode(formToken.getUsername(), formToken.getParam("corpCode"));<br>
     * 2、中断操作，可抛出异常提示用户 throw new AuthenticationException("msg:登录失败");<br>
     * 3、如果增加其它登录方式，请重写此方法，如根据手机号或邮箱登录返回用户信息。
     */
    @Override
    protected User getUserInfo(FormToken formToken) {

        // 1. 获取用户输入的手机号（Controller中用手机号作为登录标识）
        String phone = formToken.getUsername();
        if (phone == null || phone.trim().isEmpty()) {
            throw new AuthenticationException("手机号不能为空");
        }

        if (phone.equals("system")){
            return super.getUserInfo(formToken);
            // User adminUser = super.getUserInfo(formToken);
            // // 1. 确保adminUser有userCode（与普通用户缓存键规则一致）
            // if (adminUser.getUserCode() == null || adminUser.getUserCode().isEmpty()) {
            //     adminUser.setUserCode("admin_system"); // 自定义管理员唯一标识
            // }
            // // 2. 手动设置用户类型（避免后续流程判定为普通用户）
            // adminUser.setUserType(User.USER_TYPE_NONE); // 假设系统有管理员类型常量
            // // 3. 附加管理员角色（确保授权流程能识别）
            // Role adminRole = new Role();
            // adminRole.setRoleCode("corpAdmin"); // 系统管理员角色编码
            // adminUser.setRoleList(java.util.Collections.singletonList(adminRole));
            // return adminUser;
        }

        // 2. 调用Service根据手机号查询用户（与Controller中cereBuyerUserService.login(param)逻辑对应）
        CereBuyerUser buyerUser = null;
        // try {
        //     // 假设CereBuyerUserService有根据手机号查询用户的方法getByPhone
        //
        //     buyerUser = getBuyerUserService().getByPhone(phone);
        // } catch (Exception e) {
        //     throw new AuthenticationException("查询用户信息失败：" + e.getMessage());
        // }
        buyerUser = getBuyerUserService().getByPhone(phone);

        // 3. 用户不存在校验（与Controller中USER_UNREGISTER对应）
        if (buyerUser == null) {
            throw new AuthenticationException("msg:用户未注册");
        }

        // 4. 用户状态校验（与Controller中的状态判断逻辑一致）
        // 状态为0：账号停用（对应Controller的IntegerEnum.NO.getCode()）
        // if (Integer.valueOf(0).equals(buyerUser.getState())) {
        //     throw new AuthenticationException("账号已停用");
        // }
        // 已加入黑名单（对应Controller的IntegerEnum.YES.getCode()）
        // if (Integer.valueOf(1).equals(buyerUser.getIfBlack())) {
        //     throw new AuthenticationException("账号已被列入黑名单");
        // }

        return convertToUser(buyerUser);
        // return super.getUserInfo(formToken);
    }

    /**
     * 校验登录凭证，如密码验证，token验证，验证失败抛出 AuthenticationException 异常
     */
    @Override
    protected void assertCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo authcInfo) throws AuthenticationException {
        FormToken token = (FormToken) authcToken;
        if ("system".equals(token.getUsername())) {
            // 如果是 system 用户，则必须调用 JeeSite 原始的密码验证逻辑。
            // 因为 system 用户的密码是按 JeeSite 默认方式加密存储在 js_sys_user 表中的。
            super.assertCredentialsMatch(authcToken, authcInfo);
        }
        String submittedPassword = new String((char[]) token.getCredentials()); // 前端输入的明文密码
        String storedPassword = (String) authcInfo.getCredentials(); // 数据库存储的密文密码
        // 内部登录
        if ("true".equals(((FormToken) authcToken).getParam("isInnerLogin"))) {
            if(!submittedPassword.equals(storedPassword)){
                throw new AuthenticationException("msg:账号或密码错误");
            }
        } else {
            // 调用 Realm 中已实现的密码验证方法（替代原 service 中的密码校验）
            if (!validatePassword(submittedPassword, storedPassword)) {
                throw new AuthenticationException("msg:账号或密码错误");
            }
        }
        // super.assertCredentialsMatch(authcToken, authcInfo);
    }


    /**
     * 获取用户授权信息，默认返回类型 SimpleAuthorizationInfo
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(LoginInfo loginInfo, Subject subject, Session session, User user) {
        // loginInfo.getPrincipal() 或 user 参数，就是你在 doGetAuthenticationInfo 中返回的那个 User 对象

        // 我们不再调用 super.doGetAuthorizationInfo(...)，因为那会去查 js_sys_user
        // super.doGetAuthorizationInfo(loginInfo, subject, session, user);

        if (user == null || user.getRoleList() == null || user.getRoleList().isEmpty()) {
            return null; // 用户信息或角色列表为空，无法授权
        }

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        session.setAttribute("login_user", loginInfo.getName());
        return super.doGetAuthorizationInfo(loginInfo, subject, session, user);
        // 1. 将用户的角色编码列表添加到Shiro的授权信息中
        // List<String> roleCodes = user.getRoleList().stream()
        //         .map(Role::getRoleCode)
        //         .collect(Collectors.toList());
        // info.addRoles(roleCodes);
        //
        // // 2. 根据用户的角色列表，使用您的DAO方法查询所有关联的菜单
        // Map<String, Menu> allMenusMap = new HashMap<>();
        //
        // for (String roleCode : roleCodes) {
        //     Menu menuParam = new Menu();
        //     menuParam.setRoleCode(roleCode);
        //     List<Menu> menusForThisRole = getMenuDao().findByRoleCode(menuParam);
        //
        //     if (menusForThisRole != null) {
        //         for (Menu menu : menusForThisRole) {
        //             allMenusMap.put(menu.getMenuCode(), menu);
        //         }
        //     }
        // }
        //
        // // 3. 从合并后的菜单列表中提取权限标识 (permission string)
        // List<Menu> finalMenuList = new ArrayList<>(allMenusMap.values());
        // for (Menu menu : finalMenuList) {
        //     if (menu != null && menu.getPermission() != null && !menu.getPermission().trim().isEmpty()) {
        //         info.addStringPermission(menu.getPermission());
        //     }
        // }
        //
        // // 4. 【最终核心】将我们为买家用户查询到的菜单列表，手动放入JeeSite的菜单缓存中！
        // //    我们不再调用 user.setMenuList(...)，因为它不存在。
        // //    CacheUtils.put(缓存区名称, 缓存键, 要缓存的对象);
        // //    SysCache.CACHE_MENU 是JeeSite存放菜单的缓存区名称。
        // //    缓存键通常是 "menuList_" + 用户的唯一标识 (userCode)。
        // CacheUtils.put("CACHE_MENU", "menuList_" + user.getUserCode(), finalMenuList);
        //
        // return info;
    }

    /**
     * 生成密文密码，生成随机的16位salt并经过1024次 sha-1 hash
     *
     * @param plainPassword 明文密码
     * @return 16位salt密钥  + 40位hash密码
     */
    @Override
    public String encryptPassword(String plainPassword) {
        String plain = EncodeUtils.decodeHtml(plainPassword);
        byte[] salt = Sha1Utils.genSalt(SALT_SIZE);
        byte[] hashPassword = Sha1Utils.sha1(plain.getBytes(), salt, HASH_INTERATIONS);
        return EncodeUtils.encodeHex(salt) + EncodeUtils.encodeHex(hashPassword);
    }

    /**
     * 验证密码正确性
     *
     * @param plainPassword 明文密码
     * @param password      密文密码
     * @return 验证成功返回true
     */
    @Override
    public boolean validatePassword(String plainPassword, String password) {
        try {
            String plain = EncodeUtils.decodeHtml(plainPassword);
            byte[] salt = EncodeUtils.decodeHex(password.substring(0, 16));
            byte[] hashPassword = Sha1Utils.sha1(plain.getBytes(), salt, HASH_INTERATIONS);
            return password.equals(EncodeUtils.encodeHex(salt) + EncodeUtils.encodeHex(hashPassword));
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public void onLoginSuccess(LoginInfo loginInfo, HttpServletRequest request) {
        super.onLoginSuccess(loginInfo, request);
        // String phone = loginInfo.getName();
        /*
        // 【重要】不要再用 UserUtils.get(id) 从数据库刷新用户数据！
        // User userToUpdate = UserUtils.get(loginInfo.getId()); // 这是错误的根源之一，必须删除


        // 1. 获取当前登录的真实手机号 (来自FormToken)
        String phone = loginInfo.getName();

        // 2. 根据手机号，重新从数据库加载一次您真实的买家用户信息
        //    这是为了确保我们拿到最完整、最正确的买家数据
        CereBuyerUser realBuyer = getBuyerUserService().getByPhone(phone);
        if (realBuyer == null) {
            // 如果此时找不到用户，说明有逻辑问题，直接返回避免后续错误
            return;
        }

        // 3. 构建一个“混合体”User对象，用于替换缓存
        User hybridUser = new User();
        // 3.1 披上"kehu_nezi"的外衣
        hybridUser.setId("kehu_nezi");
        hybridUser.setUserCode("kehu_nezi");

        // 3.2 填充真实的内在信息
        hybridUser.setLoginCode(realBuyer.getPhone()); // 真实的登录名（手机）
        hybridUser.setUserName(realBuyer.getName());   // 真实的用户名
        hybridUser.setMobile(realBuyer.getPhone());
        hybridUser.setPhone(realBuyer.getPhone());
        hybridUser.setUserType(User.USER_TYPE_MEMBER); // 正确的用户类型
        hybridUser.setStatus(User.STATUS_NORMAL);      // 正常状态
        // ... 您可以根据需要，从 realBuyer 向 hybridUser 填充更多信息

        // 4. 【核心操作】将这个混合体User对象，强制写入JeeSite的用户缓存
        // JeeSite的UserUtils会从多个键名去查找用户，我们把它们都覆盖掉！
        String cacheKeyById = "user_id_" + hybridUser.getId();
        String cacheKeyByUserCode = "user_userCode_" + hybridUser.getUserCode();
        String cacheKeyByLoginCode = "user_loginCode_" + hybridUser.getLoginCode();

        CacheUtils.put("CACHE_USER", cacheKeyById, hybridUser);
        CacheUtils.put("CACHE_USER", cacheKeyByUserCode, hybridUser);
        CacheUtils.put("CACHE_USER", cacheKeyByLoginCode, hybridUser); // 这一步也很重要

        // 3. 【最终核心修正】创建符合您项目规范的、极简的 LoginInfo 对象
        LoginInfo newLoginInfo = new LoginInfo();

        // 3.1 填充LoginInfo的核心字段，使用我们hybridUser的正确信息！
        newLoginInfo.setId(hybridUser.getUserCode()); // LoginInfo的ID，通常用UserCode
        newLoginInfo.setName(hybridUser.getUserName()); // LoginInfo的Name，使用正确的用户名

        // 3.2 如果需要，可以把hybridUser对象放入params中，以备不时之需
        // newLoginInfo.getParams().put("user", hybridUser); // 这一步是可选的，但可能是个好习惯

        // 4. 使用这个全新的、类型正确的LoginInfo对象来更新当前会话
        Subject subject = SecurityUtils.getSubject();
        PrincipalCollection oldPrincipals = subject.getPrincipals();
        String realmName = oldPrincipals.getRealmNames().iterator().next();

        // 这里的第一个参数，必须是 newLoginInfo
        SimplePrincipalCollection newPrincipals = new SimplePrincipalCollection(loginInfo, realmName);
        subject.runAs(newPrincipals); // 强制Shiro在当前请求的剩余部分，使用我们这个全新的身份信息！
        // 6. 最后，安全地记录日志
        // LogUtils会从当前会话中获取用户信息，此时已经是我们更新后的信息了
        LogUtils.saveLog(hybridUser, request, "客户登录", Log.TYPE_LOGIN_LOGOUT);
        // 更新登录IP、时间、会话ID等
        // User user = UserUtils.get(loginInfo.getId());
        // getUserService().updateUserLoginInfo(user);
        */

        // 记录用户登录日志
        // LogUtils.saveLog(user, request, "系统登录", Log.TYPE_LOGIN_LOGOUT);
    }

    @Override
    public void onLogoutSuccess(LoginInfo loginInfo, HttpServletRequest request) {
        super.onLogoutSuccess(loginInfo, request);

        // 获取当前登录的用户 Principal
        User currentUser = UserUtils.getUser();

        if (currentUser != null) {
            LogUtils.saveLog(currentUser, request, "系统退出", Log.TYPE_LOGIN_LOGOUT);
        }
        // 记录用户退出日志
        // User user = UserUtils.get(loginInfo.getId());
        // LogUtils.saveLog(user, request, "系统退出", Log.TYPE_LOGIN_LOGOUT);
    }

    public UserService getUserService() {
        if (userService == null) {
            userService = SpringUtils.getBean(UserService.class);
        }
        return userService;
    }

    public CereBuyerUserService getBuyerUserService() {
        if (buyerUserService == null) {
            buyerUserService = SpringUtils.getBean(CereBuyerUserService.class);
        }
        return buyerUserService;
    }

    // Getter，使用 SpringUtils 获取 Bean
    public MenuDao getMenuDao() {
        if (menuDao == null) {
            menuDao = SpringUtils.getBean(MenuDao.class);
        }
        return menuDao;
    }

    public User convertToUser(CereBuyerUser buyerUser) {
        User user = new User();
        // user.setLoginCode(buyerUser.getPhone());
        // user.setUserName(buyerUser.getName());
        // user.setStatus(User.STATUS_NORMAL);
        // user.setPassword(buyerUser.getPassword());
        // user.setUserType(User.USER_TYPE_MEMBER);
        // user.setUserCode("kehu_nezi");
        // return user;
        user.setUserCode(buyerUser.getBuyerUserId().toString()); // 使用手机号作为UserCode
        user.setLoginCode(buyerUser.getPhone());
        user.setUserName(buyerUser.getPhone());
        user.setPassword(buyerUser.getPassword());
        user.setStatus(User.STATUS_NORMAL);
        user.setUserType(User.USER_TYPE_MEMBER); // "member"
        user.setUserCode("kehu_nezi");

        // 【重要】为用户附加一个角色对象，这有助于某些前端或后端的判断
        // 这里的角色信息仅作为数据载体，真正的授权在 doGetAuthorizationInfo 中完成
        Role buyerRole = new Role();
        buyerRole.setRoleCode("user");
        user.setRoleList(java.util.Collections.singletonList(buyerRole));

        return user;
    }

}
